﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace _05.TwoKeysDictionary
{
    public class TwoKeysDictionary<TKey1, TKey2, TValue> : IEnumerable<Tuple<TKey1, TKey2, TValue>>
    {
        private Dictionary<TKey1, Dictionary<TKey2, TValue>> tkDictionary;
        private int count;

        public TwoKeysDictionary()
        {
            tkDictionary = new Dictionary<TKey1, Dictionary<TKey2, TValue>>();
            count = 0;
        }

        #region Properties

        public int Count
        {
            get
            {
                return count;
            }
        }

        public TValue this[TKey1 key1, TKey2 key2]
        {
            get
            {
                if (!ContainsKeys(key1, key2))
                {
                    throw new KeyNotFoundException("The given keys was not present in the dictionary.");
                }
                else
                {
                    return tkDictionary[key1][key2];
                }
            }
        }

        public ICollection<TKey1> FirstKeys
        {
            get
            {
                return tkDictionary.Keys;
            }
        }

        public ICollection<TValue> Values
        {
            get
            {
                List<TValue> values = new List<TValue>();
                foreach (KeyValuePair<TKey1, Dictionary<TKey2, TValue>> pair in tkDictionary)
                {
                    foreach (KeyValuePair<TKey2, TValue> innerPair in pair.Value)
                    {
                        values.Add(innerPair.Value);
                    }
                }
                return values;
            }
        }

        #endregion

        #region Methods

        public void Add(TKey1 key1, TKey2 key2, TValue value)
        {
            if (ContainsKeys(key1, key2))
            {
                throw new ArgumentException("An item with the same two keys has already been added.");
            }
            else
            {
                if (!tkDictionary.ContainsKey(key1))
                {
                    tkDictionary[key1] = new Dictionary<TKey2, TValue>();
                }
                tkDictionary[key1].Add(key2, value);
                count++;
            }
        }

        public bool ContainsKeys(TKey1 key1, TKey2 key2)
        {
            if (tkDictionary.ContainsKey(key1))
            {
                return tkDictionary[key1].ContainsKey(key2);
            }
            else
            {
                return false;
            }
        }

        public bool Remove(TKey1 key1, TKey2 key2)
        {
            if (ContainsKeys(key1, key2))
            {
                count--;
                tkDictionary[key1].Remove(key2);
                return true;
            }
            else
            {
                return false;
            }
        }

        public bool TryGetValue(TKey1 key1, TKey2 key2, out TValue value)
        {
            if (ContainsKeys(key1, key2))
            {
                value = tkDictionary[key1][key2];
                return true;
            }
            else
            {
                value = default(TValue);
                return false;
            }
        }

        public void Clear()
        {
            tkDictionary.Clear();
            count = 0;
        }
        #endregion

        #region IEnumerable

        public IEnumerator<Tuple<TKey1, TKey2, TValue>> GetEnumerator()
        {
            foreach (var item in tkDictionary)
            {
                foreach (var it in item.Value)
                {
                    yield return Tuple.Create(item.Key, it.Key, it.Value);
                }
            }
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return ((IEnumerable<Tuple<TKey1, TKey2, TValue>>)this).GetEnumerator();
        }

        #endregion
    }

    class Program
    {
        static StringBuilder output = new StringBuilder();

        static void StartUniversityCommands()
        {
            TwoKeysDictionary<string, string, string> allStudentInSofiaUniversity = new TwoKeysDictionary<string, string, string>();
            string line = Console.ReadLine();
            while (line != String.Empty)
            {
                string[] separators = { " | " };
                string[] command = line.Split(separators, StringSplitOptions.RemoveEmptyEntries);
                ЕxecuteCommand(command, allStudentInSofiaUniversity);
                line = Console.ReadLine();
            }
        }

        static void StartUniversityCommands(string fileName)
        {
            StreamReader reader = new StreamReader(fileName);
            TwoKeysDictionary<string, string, string> allStudentInSofiaUniversity = new TwoKeysDictionary<string, string, string>();

            using (reader)
            {
                string line = reader.ReadLine();
                while (line != String.Empty)
                {
                    string[] separators = { " | " };
                    string[] command = line.Split(separators, StringSplitOptions.RemoveEmptyEntries);
                    ЕxecuteCommand(command, allStudentInSofiaUniversity);
                    line = reader.ReadLine();
                }
            }
        }

        static void ЕxecuteCommand(string[] command, TwoKeysDictionary<string, string, string> dictionary)
        {
            switch (command[0])
            {
                case "Add": dictionary.Add(command[1], command[2], command[3]); break;
                case "Remove": dictionary.Remove(command[1], command[2]); break;
                case "Clear": dictionary.Clear(); break;

                case "Count": output.AppendLine(dictionary.Count.ToString()); break;
                case "Get": output.AppendLine(dictionary[command[1], command[2]]); break;
                case "ContainsKey": output.AppendLine(dictionary.ContainsKeys(command[1], command[2]).ToString()); break;
                case "Foreach":
                    {
                        foreach (var item in dictionary)
                        {
                            output.AppendLine(String.Format("{0} {1} {2}", item.Item1, item.Item2, item.Item3));
                        }
                        break;
                    }
            }
        }

        static void PrintOutput()
        {
            Console.Write(output);
        }

        static void PrintOutput(string fileName)
        {
            StreamWriter writer = new StreamWriter(fileName);
            using (writer)
            {
                writer.Write(output);
            }
        }

        static void Main(string[] args)
        {
            //TestGenerator.CreateTest("test18.in.txt");

            StartUniversityCommands(@"..\..\Tests\test10.in.txt");
            PrintOutput(@"..\..\Tests\test10.out.txt");

            //StartUniversityCommands();
            //PrintOutput();
        }
    }
}